/*
    从二进制文件中读取数据，反序列化到通讯录对象中，实现打印所有联系人的方法
*/
#include <iostream>
#include <fstream>
#include "address_book.pb.h"

// 打印所有联系人
void PrintContacts(const addressbook::AddressBook& addressBook)
{
    int n = addressBook.contacts_size();
    for(int i = 0; i < n; i++)
    {
        const addressbook::Contacts& ct = addressBook.contacts(i);  
        std::cout << "-----------------联系人" << i+1 << "-----------------" << std::endl;
        std::cout << "姓名：" << ct.name() << std::endl;
        std::cout << "年龄：" << ct.age() << std::endl;
        
        // 未知字段：对于结构正确的protobuf数据，在反序列化的时候不能正确匹配的字段叫做未知字段
        // proto 3.5 语法版本之后对于未知字段会保存起来，之前的语法版本会直接丢弃
        // proto 处理未知字段的原理见readme.md
        const google::protobuf::Reflection* reflection = ct.GetReflection(); // 获取映射
        const google::protobuf::UnknownFieldSet& unknownFieldSet = reflection->GetUnknownFields(ct); // 根据对象获取其未知字段集合
        int n = unknownFieldSet.field_count(); // 该对象中未知字段的个数
        for(int i = 0; i < n; i++) // 遍历集合中每个未知字段
        {
            const google::protobuf::UnknownField& unknownField = unknownFieldSet.field(i); // 根据下标获取字段
            int fieldNumber = unknownField.number(); // 获取字段编号
            std::cout << "[未知字段" << i+1 << "]" << "编号:" << fieldNumber << ", 值:"; 
            google::protobuf::UnknownField::Type type = unknownField.type(); // 获取字段类型，枚举类型
            // 根据字段类型分别调用不同的函数获取值
            int value = 0;
            switch(type)
            {
            case google::protobuf::UnknownField::TYPE_VARINT: 
                std::cout << unknownField.varint() << std::endl;
                break;
            case google::protobuf::UnknownField::TYPE_LENGTH_DELIMITED:
                std::cout << unknownField.length_delimited() << std::endl;
                break;
            }
        }
    }
}
int main()
{
    // 1. 打开文件，如果存在从文件中加载数据
    std::fstream input("../address_book.bin", std::ios::in | std::ios::binary);
    addressbook::AddressBook addressBook;
    if(input) // 文件存在
    {
        bool ret = addressBook.ParseFromIstream(&input); // 从文件流中反序列化
        input.close();
        if(ret == false)
        {
            std::cout << "从文件中反序列化通讯录失败" << std::endl;
            return -1;
        }
    }
    else
    {
        std::cout << "文件不存在" << std::endl;
        return -1;
    }

    // 2. 打印所有联系人
    PrintContacts(addressBook);
    return 0;
}